package com.curluin.approximateShapes;

import java.util.Vector;

import com.curluin.math.MyVector;
import com.curluin.math.Vertex;
import com.curluin.scene.Node;

public class Contour extends ApproximateShape {

	private String LOG_TAG = "Contour";
	public MyVector center;
	float xMin = Float.POSITIVE_INFINITY;
	float xMax = Float.NEGATIVE_INFINITY;
	/**
	 * Corners of the Contour
	 */
	Vector<MyVector> contourCoords;

	public Contour(int numBins, Node node){
		this.center=node.getCenter();
		contourCoords = getCorners(node.getVertices(), numBins);
	}


	@Override
	protected Vector<MyVector> initializeScanData(float sampleRate) {
		Vector<MyVector> res = new Vector<MyVector>();
		float xAct=xMin;
		float xmax=xMax;
		
		while(xAct<xmax){
			res.addAll(computeSingleXScan(xAct, contourCoords));
			xAct+=sampleRate;
		}
		res.addAll(computeSingleXScan(xmax, contourCoords));
		
		for(int i = 0; i<contourCoords.size(); i++){
			MyVector[] edge = getEdge(i, contourCoords);
			res.add(edge[0]);
			res.add(edge[1]);
		}
		scanData=res;
		translateScanData(center);
		contourCoords.trimToSize();
		return scanData;
	}

	/**
	 * Computes all z-coordinates of the shape lying on the specified x-coordinate,
	 * the line between two returned vectors is occupied by the shape (even=start points,uneven=end points).
	 * @param x
	 * @return vectors, each two define a line
	 */
	private Vector<MyVector> computeSingleXScan(float x, Vector<MyVector> contourCoords) {
		Vector<MyVector> res = new Vector<MyVector>();

		for(int i = 0; i<contourCoords.size(); i++){
			MyVector[] edge = getEdge(i, contourCoords);
			//			Log.e(LOG_TAG, "Edge: "+edge[0]+"---"+edge[1]);
			if(edge[0].get(0)==x&&x==edge[1].get(0)){
				res.add(edge[0]);
				res.add(edge[1]);
				//				Log.e(LOG_TAG, "Add 2 interceptions(same x): "+x+" "+edge[0]+" "+edge[1]);
			}
			else{
				MyVector interPoint = getXInterception(x, edge);
				if(interPoint != null){
					//					Log.e(LOG_TAG, "Add interception: "+x+" "+interPoint);
					res.add(interPoint);
				}
			}
		}
		//		Log.e(LOG_TAG, "interceptions: "+res);
		return res;
	}

	/**
	 * Edge of Countour
	 * @param num
	 * @param contourCoords
	 * @return
	 */
	private MyVector[] getEdge(int num, Vector<MyVector> contourCoords){
		MyVector[] res = new MyVector[2];
		int idx2 = (num+1)%contourCoords.size();
		res[0]=contourCoords.get(num);
		res[1]=contourCoords.get(idx2);
		return res;
	}

	/**
	 * Interception of an edge and specified x coordinate
	 * @param x
	 * @param edge
	 * @return
	 */
	private MyVector getXInterception(float x, MyVector[] edge){
		MyVector res = null;

		if(xbetweenPoints(x, edge[0], edge[1])){
			MyVector dirVec = edge[1].subtract(edge[0]);
			float factor = (x-edge[0].get(0))/dirVec.get(0);
			float z = edge[0].get(2)+factor*dirVec.get(2);
			res=MyVector.makeVector3(x, 0, z);
		}
		//		Log.e(LOG_TAG, "interception?: "+x+" "+edge[0]+" "+edge[1]+" "+res);
		return res;
	}

	/**
	 * Checks if x is between the x-coordinates of p1 and p2
	 * @param x
	 * @param p1
	 * @param p2
	 * @return
	 */
	private boolean xbetweenPoints(float x, MyVector p1, MyVector p2){
		if(p1.get(0)>p2.get(0)) return xbetweenPoints(x, p2, p1);
		return (p1.get(0)<x)&&(x<p2.get(0));
	}

	/**
	 * Returns an Approximation of the Contour of the Object (given by vertices) 
	 * by getting the from the center most distant coordinates of the Object.
	 * Assumes that the center of the object is [0 0 0].
	 * @param vertices
	 * @param numBins
	 * @return
	 */
	private Vector<MyVector> getCorners(Vector<Vertex>  vertices, int numBins){

		float binSize = 360.0f/numBins;
		Vector<MyVector> res = new Vector<MyVector>();
		float[] mostDistantDist= new float[numBins];
		MyVector[] mostDistantVec= new MyVector[numBins];
		float dist,angle;
		MyVector pos;
		MyVector normPos;
		int idx;
		for(Vertex v : vertices){
			pos = MyVector.makeVector3(v.coords.get(0), v.coords.get(1), v.coords.get(2));
			pos.set(1, 0);
			dist = (pos).getNorm();
			normPos =  MyVector.makeCopy(pos);
			normPos.normalize();
			angle =(float) (Math.atan2(normPos.get(2), normPos.get(0))*180/Math.PI);
			if(angle<0) angle=360+angle;
			idx=(int) (angle/binSize);
			if(dist>mostDistantDist[idx]){
				mostDistantDist[idx] = dist;
				mostDistantVec[idx] = pos;
			}

		}
		for(int i=0; i<mostDistantVec.length;i++){
			if(mostDistantVec[i]!=null){
				res.add(mostDistantVec[i]);
				xMin = Math.min(res.get(i).get(0), xMin);
				xMax = Math.max(res.get(i).get(0), xMax);
			}
		}
		return res;
	}
}
